package com.luoqiz.demo.reflect;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.UUID;

import org.junit.Test;

import com.luoqiz.demo.model.Father;
import com.luoqiz.demo.model.Person;

public class ReflectDemo {

	@Test
	public void newInstanceTest() {
		System.out.println("***************************************************************************");
		System.out.println("创建实例对象测试");
		Class<Person> clazz = Person.class;
		try {
			System.out.println("类名：" + clazz.getName());
			System.out.println("简单类名：" + clazz.getName());
			System.out.println("-----------------------");
			System.out.println("通过反射实例化对象");
			Object p = clazz.newInstance();
			System.out.println(p.getClass());
		} catch (InstantiationException | IllegalAccessException e) {
			e.printStackTrace();
		}
		System.out.println();
	}

	@Test
	public void constructorTest() throws InstantiationException, IllegalAccessException, NoSuchMethodException,
			SecurityException, IllegalArgumentException, InvocationTargetException {
		System.out.println("***************************************************************************");
		System.out.println("构造函数测试");
		Class<Person> clazz = Person.class;
		Constructor<?>[] cs = clazz.getConstructors();
		System.out.println("循环显示构造函数");
		for (Constructor<?> c : cs) {
			System.out.println("-----------------------");
			if (c.getParameterCount() == 0) {
				System.out.println("空构造函数：构造函数所需的参数个数" + 0);
				System.out.println("测试空构造");
				Person a = clazz.newInstance();
				System.out.println(a.toString());
			}
			if (c.getParameterCount() == 1) {
				System.out.println("参数构造函数：构造函数所需的参数个数" + 1);
				System.out.println("测试参数构造函数，需要得到带参数的构造函数描述符");
				Constructor<Person> cont = clazz.getConstructor(String.class);
				Person b = cont.newInstance("luoqiz小罗");
				System.out.println(b.toString());
			}
		}
		System.out.println();
	}

	@Test
	public void methodTest() throws NoSuchMethodException, SecurityException, InstantiationException,
			IllegalAccessException, IllegalArgumentException, InterruptedException {
		System.out.println("***************************************************************************");
		System.out.println("方法描述符测试");
		Class<Person> clazz = Person.class;
		Method[] declaredMethods = clazz.getDeclaredMethods();
		System.out.println("getDeclaredMethods方法只获取对象本身拥有的方法，不包含父对象方法");
		for (Method m : declaredMethods) {
			System.out.print(m.getName() + ",");
		}
		System.out.println();
		System.out.println("-----------------------");
		System.out.println("getMethods方法获取对象本身以及父对象拥有的方法");
		Method[] methods = clazz.getMethods();
		for (Method m : methods) {
			System.out.print(m.getName() + ",");
		}
		System.out.println();
		System.out.println("-----------------------");

		System.out.println("测试方法的执行情况");
		Constructor<Person> cont = clazz.getConstructor(String.class);
		Person obj = null;
		try {
			obj = cont.newInstance("小罗");
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
		Method m = clazz.getDeclaredMethod("setAge", int.class);
		try {
			m.invoke(obj, 150);
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
		System.out.println("反射执行setAge方法后的值：" + obj.getAge());

		System.out.println("-----------------------");
		System.out.println("测试私有方法的执行情况，需要设置可访问");
		Method m1 = clazz.getDeclaredMethod("testPrivate");
		try {
			m1.invoke(obj);
		} catch (InvocationTargetException e) {
			System.err.println("私有方法访问出错");
			e.printStackTrace();
		} finally {
			System.err.println("私有方法访问出错");
			Thread.sleep(1000);
			try {
				m1.setAccessible(true);
				m1.invoke(obj);
			} catch (InvocationTargetException e) {
				e.printStackTrace();
			}
			System.out.println();
		}
	}

	@Test
	public void filedTest() {
		System.out.println("***************************************************************************");
		System.out.println("字段描述符测试");
		Class<Person> clazz = Person.class;
		System.err.println("getDeclaredFields方法获取所有的字段");
		Field[] declaredFields = clazz.getDeclaredFields();
		for (Field field : declaredFields) {
			System.out.print(field.getName() + ",");
		}
		System.out.println();
		System.out.println("-----------------------");
		System.err.println("getFields方法获取只能获取public的字段");
		Field[] fields = clazz.getFields();
		for (Field field : fields) {
			System.out.print(field.getName());
		}
		System.out.println("-----------------------");
		System.err.println("getField方法获取name字段,并赋值");
		Field field = null;
		Person o = null;
		try {
			o = clazz.newInstance();
		} catch (InstantiationException e1) {
			e1.printStackTrace();
		} catch (IllegalAccessException e1) {
			e1.printStackTrace();
		}
		try {
			field = clazz.getDeclaredField("name");
			field.set(o, "小罗");
		} catch (NoSuchFieldException e) {
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			System.err.println("name字段私有无法访问");
			System.out.println("name字段设置可访问性");
			try {
				field.setAccessible(true);
				field.set(o, "小罗");
			} catch (IllegalArgumentException | IllegalAccessException e1) {
				e1.printStackTrace();
			}
			System.out.println("获取name的值：" + o.getName());
			// e.printStackTrace();

		}

		Field field1 = null;
		try {
			System.out.println("uid字段属性public");
			field1 = clazz.getField("uid");
			field1.set(o, UUID.randomUUID() + "");
			System.out.println("获取uid字段值：" + o.uid);
		} catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException e) {
			e.printStackTrace();
		}
		System.out.println();
	}

	@Test
	public void modifierTest() {
		System.out.println("***************************************************************************");
		System.out.println("修飾描述符测试");
		Class<Person> clazz = Person.class;
		Field[] fields = clazz.getDeclaredFields();
		for (Field field : fields) {
			int modifier = field.getModifiers();
			if (Modifier.isPrivate(modifier)) {
				System.out.println(field.getName() + "---字段的描述符:private");
			}
			if (Modifier.isPublic(modifier)) {
				System.out.println(field.getName() + "---字段的描述符:public");
			}
		}
		System.out.println();
	}

	@Test
	public void getClassesTest() {
		// Class<Person> clazz=Person.class;
	}

	@Test
	public void propertyCopyTest() {
		System.out.println("***************************************************************************");
		System.out.println("对象深拷贝");
		// 源对象
		Person srcPerson = new Person();
		srcPerson.setAge(50);
		srcPerson.setName("小罗");
		srcPerson.setHeight(60);

		// 目标对象
		Person destPerson = new Person();

		Class<Person> clazz = Person.class;
		Field[] fields = clazz.getDeclaredFields();
		try {
			for (Field field : fields) {
				if (!field.isAccessible())
					field.setAccessible(true);
				Object value = field.get(srcPerson);
				field.set(destPerson, value);
			}
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}
		System.out.println(destPerson.toString());
		System.out.println();
	}

	@Test
	public void introspectorTest() {
		System.out.println("***************************************************************************");
		System.out.println("内省复制对象家族树，内省是根据set、get方法解析属性，属性和字段是不同的概念");

		// 源对象
		Father srcFather = new Father();
		srcFather.setAge(50);
		srcFather.setName("小罗");
		srcFather.setHeight(60);
		srcFather.setHostCount(160);

		// 目标对象
		Father destFather = new Father();

		BeanInfo beanInfo;
		try {
			beanInfo = Introspector.getBeanInfo(Father.class);
			PropertyDescriptor[] properties = beanInfo.getPropertyDescriptors();
			for (PropertyDescriptor property : properties) {
				// 获取getter方法
				Method getter = property.getReadMethod();
				// 获取setter方法
				Method setter = property.getWriteMethod();
				if (getter == null || setter == null) {
					continue;
				}
				Object value = getter.invoke(srcFather);
				setter.invoke(destFather, value);
			}
		} catch (IntrospectionException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}

		System.out.println(destFather.toString());
		System.out.println();
	}

	@Test
	public void inTest() {
		System.out.println("***************************************************************************");
		System.out.println("内省是根据set、get方法解析属性，属性和字段是不同的概念");
	}
}
